#pragma once
#include<cstddef>
#include<iostream>
#include<ostream>
#include<queue>
#include<cassert>
#include<pthread.h>
#include<unistd.h>
#include"lock.hpp"
using std::queue;
using std::cout;
using std::endl;
#define THREADNUM 5

template<class T>
class threadPool{
    public:
        static threadPool<T>* getInstance()
        {
            static Mutex mutex;
            if(_instance==nullptr)
            {
                LockGuard lockG(&mutex);
                if(_instance==nullptr)
                {
                    _instance=new threadPool<T>();
                }
            }
            return _instance;
        }
        static void* threadRountine(void* args)
        {
            pthread_detach(pthread_self());
            threadPool<T>* tP=static_cast<threadPool<T>*>(args);
            while(true)
            {
                tP->LockQueue();
                while(!tP->haveTask())
                {
                    tP->waitForTask();
                }
                T task=tP->popTask();
                tP->unlockQueue();
                task.run();
            }
        }
        void start()
        {
            try{
                if(_isStart)
                    throw"Error: thread pool already exists";
            }
            catch(const char* e)
            {
                cout<<e<<endl;
                return;
            }
            for(int i=0;i<_threadNum;i++)
            {
                pthread_t temp;
                pthread_creat(&temp,nullptr,threadRountine,this);
            }
            _isStart=true;
        }
        void pushTask(const T& in)
        {
            lockQueue();
            _taskQueue.push(in);
            choiceThreadForHandler();
            unlockQueue();
        }

        int getThreadNum()
        {
            return _threadNum;
        }
        ~threadPool()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_cond);
        }
        threadPool(const threadPool<T>&)=delete;
        threadPool<T>& operator=(const threadPool<T>&)=delete;
    private:
        threadPool(size_t threadNum=THREADNUM)
            :_threadNum(threadNum)
             ,_isStart(false){
             assert(_threadNum>0);

             pthread_mutex_init(&_mutex,nullptr);
             pthread_mutex_init(&_cond,nullptr);
             }
        void lockQueue()
        {
            pthread_mutex_unlock(&_mutex);
        }
        void unlockQueue()
        {
            pthread_mutex_unlock(&_mutex);
        }
        bool haveTask()
        {
            return !_taskQueue.empty();
        }
        void waitForTask()
        {
            pthread_cond_wait(&_cond,&_mutex);
        }
        T popTask()
        {
            T task=_taskQueue.front();
            _taskQueue.pop();
            return task;
        }
        void choiceThreadForHandler()
        {
            pthread_cond_signal(&_cond);
        }
    private:
        size_t _threadNum;
        bool _isStart;
        queue<T> _taskQueue;
        pthread_mutex_t _mutex;
        pthread_cond_t _cond;

        static threadPool<T>* _instance;
            
                
};
